<?php
/**
 * Created by PhpStorm.
 * User: yunzhi.liu
 * Date: 2020/9/25
 * Time: 10:33
 */

namespace app\strategy\pay;

use app\common\OrderStatus;
use app\models\order\OrderModel;
use app\models\user\UserModel;
use app\services\config\ConfigService;
use app\services\order\OrderService;
use app\services\user\DetailMoneyService;
use app\strategy\pay\PayInterface;
use think\facade\Db;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

class WxPay implements PayInterface
{
    private $appId;
    private $mchId;
    private $certNo;
    private $privateKeyPath;
    private $platformKeyPath;
    private $merchantPrivateKeyInstance;

    public function __construct()
    {
        $config = ConfigService::getConfigValueArray(['wechat_app_id', 'mch_id', 'cert_no', 'mch_private_key', 'platform_cert']);
        $this->appId = $config['wechat_app_id'];
        $this->mchId = "1675637780";
        $this->certNo = $config['cert_no'];
//        $this->privateKeyPath = $config['mch_private_key'];
//        $this->platformKeyPath = $config['platform_cert'];
        $this->privateKeyPath = "-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQD1RB1tRZtSGaxF
4BlFMcEvUOe7Z7kdTum8jusKc/5EGaJU2iBzWl4EzGO4YYBR014dFl+a1ktKo4+m
5qqI6i7sNFtiY0SE1BdDXmUtHgkhZIcAU4+RMwm0rKmRMh1evfIau/1ZPH7MtbiU
cWvS3AtjNfBf1oro10z8XIvYGCNtGEPVBq7alQilMx9Pzzln+BbUm2CHoUvwPfAi
c2eNGjY6bmuafQHAoY0h0Bi5+JG5IXXNE2kdmyTw+XC3V5SCe5KnTLg9oUwOIUrF
uuwp6pWV0flTdaRbKLTBVXUOnEcubRaKfjhfcAe+6ZEgJ4RBw/+tmBfz1KisMifW
uYxYU0OVAgMBAAECggEBALrW8efdP85SWmc8qWOJ3JXz9yRPhrhp9is7YJgs9sTg
Fka3iEL302mqXkkGlZ8/+pEECz7EAsVQRk2pqL32UFqGJBuWbYsWmsYqcb7mA6SZ
lTJNda/yBnHJmr9OQwt6faWaEgzZxeqigIMsyCvwjwg7/eq0YEuyQCZ7RdkPwmUG
ulduJh9YZjYdXpbBFjlyeaULifU6lGZnLg0VjyKHuUGXP0sHisaAz/I5lXnt/+mE
hLlU3GxG97e/leVnxSOBdT+qZwfkQyf4fI/9UA/8qmmBqIClPbljMJ6cYE/3jyX+
/BIOyndO+0eZmUTRMk3tI4ZeBTvAhAGVONWgPy/cvz0CgYEA+wxBbrVRyA75mLV/
xSyi3LPo1BekHvv7qoBmlfUWAEXFV+crsBL0RSxtN+0r9KztG/8ugxhYET+fSV45
VnELFAFKxMHs9SrVstFahT7O5JMT7OBM4O3t1V9kV5YwLmDByr0JIKeHYkqwUhxa
ERJnNV2MJ2kfvCJ96PWJ3qxFDwcCgYEA+hqpkDZA0aQ1x2qJjlbYMrXdvoaj7ha2
x/UCA3hs61ifSe30Im/QOqg41nRuayhfsvXQujlnTIjiA4K/FxikCh78rm/15Y3v
um5Fzz6vkSA3SvhgwtGB7p4z15f1TZXC33WYWRSgGhHyaGNYLIO4kYqaIe7TgbuT
KNyy+AH3FYMCgYEAj3T/Zw4B/7pe8t60dq39GvVL1Pb1eCcyNspM1OBKhDlafdLB
6knKgETU3KKQ/a5biCYYJB2jJagfk8NeLIFIfVQHr8vJZofludtcSMwNWhyZxECg
beBIiieKpWpTeg0ccIf5UUmqWYaonyNIbH0BsUMVteeilNV48cht0mrKw70CgYEA
0Ia+bcDuVlylgjjvq7/4zn8X/SzhLTe++gTrwtz7Y1YmqUs+zB+R6/YqPZQiJDIr
aFjPyRTnbcaLOwYeMTGYQ+AGKaKr0NOA113t0pefZk8ICvgAJS7tOHirHMxT+HYv
XoT7mvo5asMf66047RR+JMr9K+Gmc3pjfiRVC7cHveUCgYEAsHEvXMAFQ2QNDAKy
6vGVsscswsnMeTpZYjX2ZfpikwNcVZXtKDubqAjI1+mQn26hZiS0Fm5e5+OtkGzz
BWm2wuW8xl8kXGlVOKlODuR/i5rYn1rJGZAcf/t+hMMavxQQG3wRppM2NY4IcueJ
azTareNmK2r3s6NTXVs+ByeKXSc=
-----END PRIVATE KEY-----
";
        $this->platformKeyPath = "-----BEGIN CERTIFICATE-----
MIIEITCCAwmgAwIBAgIUNyDOqyj4mLgeHbfYQYlYwG6SISMwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjQwNTA2MDkyMjA1WhcNMjkwNTA1MDkyMjA1WjB7MRMwEQYDVQQDDAox
Njc1NjM3NzgwMRswGQYDVQQKDBLlvq7kv6HllYbmiLfns7vnu58xJzAlBgNVBAsM
HuWuieW+veWImOicnOWVhui0uOaciemZkOWFrOWPuDELMAkGA1UEBhMCQ04xETAP
BgNVBAcMCFNoZW5aaGVuMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
9UQdbUWbUhmsReAZRTHBL1Dnu2e5HU7pvI7rCnP+RBmiVNogc1peBMxjuGGAUdNe
HRZfmtZLSqOPpuaqiOou7DRbYmNEhNQXQ15lLR4JIWSHAFOPkTMJtKypkTIdXr3y
Grv9WTx+zLW4lHFr0twLYzXwX9aK6NdM/FyL2BgjbRhD1Qau2pUIpTMfT885Z/gW
1Jtgh6FL8D3wInNnjRo2Om5rmn0BwKGNIdAYufiRuSF1zRNpHZsk8Plwt1eUgnuS
p0y4PaFMDiFKxbrsKeqVldH5U3WkWyi0wVV1DpxHLm0Win44X3AHvumRICeEQcP/
rZgX89SorDIn1rmMWFNDlQIDAQABo4G5MIG2MAkGA1UdEwQCMAAwCwYDVR0PBAQD
AgP4MIGbBgNVHR8EgZMwgZAwgY2ggYqggYeGgYRodHRwOi8vZXZjYS5pdHJ1cy5j
b20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3
NTQ5ODQ2QzAxQzNFOEVCRDImc2c9SEFDQzQ3MUI2NTQyMkUxMkIyN0E5RDMzQTg3
QUQxQ0RGNTkyNkUxNDAzNzEwDQYJKoZIhvcNAQELBQADggEBAFucOVW/PqnEGAbd
GJbpmSw0Xth1AvXq3gd1S06p8vZe+Z9c8F6FVXsdxrkXbL5tqd0Oi1l/oTRF7UE5
W8k5oikxD7cYCltgE1FY9s1Fr7Mdfyi4/fiX+v4OPZuwBDodge1aKZI9jHzj7Xwz
MpILIT5VH+aa9xq6L4SIK1DFDazvm/T/2oizeZbIeWbP9hRBJgYscGZRYiiXzoYJ
Vi4M+mzsq3S70V57r4+gMLfVZGEfzzS4aq//VNs4TvNQrldTn5lIcV0BkfxzVRLB
IOl93cp/nK59xNY3gqBVM9BoORCwmULnlxpklJuj7lK7VJf/Snwm4hOzK2yj7Qu8
89UT+W8=
-----END CERTIFICATE-----
";
    }

    /**
     * 获取实例
     * @return \WeChatPay\BuilderChainable
     * @throws \Exception
     */
    public function getInstance()
    {
        try {
            // 商户号
            $merchantId = $this->mchId;

            // 从本地文件中加载「商户API私钥」，「商户API私钥」会用来生成请求的签名
            //$merchantPrivateKeyFilePath = 'file://'.public_path().'wx_cert/apiclient_key.pem';
            $merchantPrivateKeyFilePath = $this->privateKeyPath;

            $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
            $this->merchantPrivateKeyInstance = $merchantPrivateKeyInstance;

            // 「商户API证书」的「证书序列号」
            //$merchantCertificateSerial = '3C82D6F93E2410D94064A2E508A1C51812880194';
            $merchantCertificateSerial = $this->certNo;

            // 从本地文件中加载「微信支付平台证书」，用来验证微信支付应答的签名
            //$platformCertificateFilePath = 'file://'.public_path().'merchant_cert/wechatpay_445D4A5C5949F0E28D020E16F4C87BBA3BDA8D03.pem';
            $platformCertificateFilePath = 'file://'.public_path().'merchant_cert/wechatpay_445D4A5C5949F0E28D020E16F4C87BBA3BDA8D03.pem';
            $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

            // 从「微信支付平台证书」中获取「证书序列号」
            $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

            // 构造一个 APIv3 客户端实例
            $instance = Builder::factory([
                'mchid' => $merchantId,
                'serial' => $merchantCertificateSerial,
                'privateKey' => $merchantPrivateKeyInstance,
                'certs' => [
                    $platformCertificateSerial => $platformPublicKeyInstance,
                ],
            ]);
            return $instance;
        } catch (\Exception $exception) {
            throw new \Exception($exception->getMessage());
        }
    }

    /**
     * 微信jsapi支付
     * @param $userId
     * @param $orderSn
     * @param $amount
     * @param $subject
     * @param $notifyUrl
     * @return array
     * @throws \Exception
     */
    public function pay($userId, $orderSn, $amount, $subject, $notifyUrl)
    {
        try {
            $instance = $this->getInstance();


            $userInfo = UserModel::find($userId);
            $openID = $userInfo['open_id'];

            $resp = $instance
                ->chain('v3/pay/transactions/jsapi')
                ->post(['json' => [
                    'mchid' => $this->mchId,
                    'out_trade_no' => $orderSn,
                    'appid' => $this->appId,
                    'description' => $subject,
                    'notify_url' => $notifyUrl,
                    'amount' => [
                        'total' => 1,
                        'currency' => 'CNY'
                    ],
                    'payer' => [
                        'openid' => $openID
                    ],
                ]]);
            $status = $resp->getStatusCode();
            $body = $resp->getBody();
            $result = json_decode($body->getContents(), true);
            $prepay_id = $result['prepay_id'];
            $params = [
                'appId' => $this->appId,
                'timeStamp' => (string)Formatter::timestamp(),
                'nonceStr' => Formatter::nonce(),
                'package' => 'prepay_id=' . $prepay_id,
            ];
            $params += ['paySign' => Rsa::sign(
                Formatter::joinedByLineFeed(...array_values($params)),
                $this->merchantPrivateKeyInstance
            ), 'signType' => 'RSA'];

            return $params;
        } catch (\Exception $e) {
            // 进行错误处理
            throw new \Exception($e->getMessage());
        }
    }

    public function refund($orderId, $cancelType)
    {
        try {
            $instance = $this->getInstance();

            //查找订单信息
            $orderInfo = OrderModel::find($orderId);
            //计算退款金额
            $amount = OrderService::getOrderRefundAmount($orderInfo);
            if ($amount <= 0) {
                return true;
            }
            // 退给客户的金额
            $refundAmount = $amount['return_money'];
            // 技师获得金额
            $techGetAmount = $amount['tech_get_money'];

            $total = bcmul($orderInfo['pay_amount'], 100, 0);
            $amount = bcmul($refundAmount, 100, 0);

            $outRefundNo = createOrderSn('TK');
            $resp = $instance
                ->chain('/v3/refund/domestic/refunds')
                ->post([
                    'json' => [
                        'transaction_id' => $orderInfo['transaction_id'],
                        'out_refund_no' => $outRefundNo,
                        'reason' => '订单取消',
                        'amount' => [
                            'total' => 1,
                            'currency' => 'CNY',
                            'refund' => 1,
                        ],
                    ]]);

            $status = $resp->getStatusCode();
            $body = $resp->getBody();
            $result = json_decode($body->getContents(), true);
            if ($result['status'] != 'SUCCESS' && $result['status'] != 'PROCESSING') {
                throw new \Exception('退款失败，请联系管理员');
            }
            //退款成功 修改状态
            $orderInfo->refund_transaction_id = $result['refund_id'];
            $orderInfo->refund_price = $refundAmount;
            $orderInfo->out_refund_no = $outRefundNo;
            $orderInfo->order_status = OrderStatus::ORDER_CANCEL;
            $orderInfo->cancel_time = time();
            $orderInfo->cancel_type = $cancelType;
            $orderInfo->save();

            // 技师余额变更记录
            if ($techGetAmount > 0) {
                $type = DetailMoneyService::REFUND_GET_FEE;
                DetailMoneyService::techCreate($orderInfo['tec_id'], $type, $techGetAmount, 0, $orderInfo['order_sn'], DetailMoneyService::getTypeText($type));
            }
            return true;
        } catch (\Exception $e) {
            // 进行错误处理
            throw new \Exception($e->getMessage());
        }

    }
}